import * as vscode from 'vscode';
import { I18nService } from '../services/i18nService';

interface DocumentCache {
    version: number;
    matches: Array<{
        key: string;
        startIndex: number;
        endIndex: number;
        line: number;
    }>;
}

/**
 * I18n 定义提供器，支持内联翻译显示
 * 
 * 主要问题及解决方案：
 * 1. 文本重叠问题：
 *    - 问题：翻译文本与原始文本重叠显示
 *    - 解决方案：使用两个装饰器 - 一个用于隐藏原文，一个用于显示翻译
 * 
 * 2. 红色字母 'a' 显示问题：
 *    - 问题：翻译文本后出现多余的红色字母 'a'
 *    - 原因：t("key") 调用的正则表达式匹配不完整
 *    - 解决方案：修正正则表达式和范围计算，确保完全覆盖整个调用
 * 
 * 3. 装饰器样式问题：
 *    - 问题：VS Code 不支持某些 CSS 属性（display: none, width: 0）
 *    - 解决方案：使用支持的属性组合来隐藏文本
 * 
 * 4. 无效翻译显示问题：
 *    - 问题：无效翻译显示为装饰器样式，而不是红色源代码
 *    - 解决方案：为无效翻译创建单独的装饰器
 * 
 * 5. 装饰器应用顺序问题：
 *    - 问题：装饰器效果不一致
 *    - 解决方案：先应用隐藏装饰器，再应用翻译装饰器
 * 
 * 6. 点击切换显示问题：
 *    - 问题：需要在点击时显示原始文本
 *    - 解决方案：添加点击位置跟踪，在点击位置不应用装饰器
 */
export class I18nDefinitionProvider implements vscode.DefinitionProvider {
    private output: vscode.OutputChannel;

    // 问题1和3：文本重叠和装饰器样式问题
    // 解决方案：使用支持的 VS Code 属性的独立装饰器
    private translationDecorationType: vscode.TextEditorDecorationType;
    private hideOriginalDecorationType: vscode.TextEditorDecorationType;

    // 问题4：无效翻译显示问题
    // 解决方案：为未翻译文本创建单独的装饰器，只改变颜色
    private untranslatedDecorationType: vscode.TextEditorDecorationType;

    private activeEditor: vscode.TextEditor | undefined;
    private documentCache = new Map<string, DocumentCache>();

    // 问题2：正则表达式匹配范围问题
    // 解决方案：模式匹配整个 t("key") 调用，包括括号
    private static readonly T_CALL_PATTERN = /t\(['"]([\w\.]+)['"]\)/g;

    // 问题6：点击切换显示问题
    // 解决方案：跟踪最后点击位置以跳过该位置的装饰器
    private lastClickedPosition: vscode.Position | null = null;

    constructor(private i18nService: I18nService) {
        this.output = vscode.window.createOutputChannel('I18n Helper Debug');

        // 问题1解决方案第1部分：翻译文本装饰器
        this.translationDecorationType = vscode.window.createTextEditorDecorationType({
            before: {
                contentText: '',
                margin: '0'
            }
        });

        // 问题1和3解决方案第2部分：隐藏原文装饰器
        // 仅使用 VS Code 支持的属性
        // 避免使用不支持的属性如 display 或 width
        this.hideOriginalDecorationType = vscode.window.createTextEditorDecorationType({
            backgroundColor: 'transparent',
            color: 'transparent',
            textDecoration: 'none;',
            letterSpacing: '-1000em',
            opacity: '0'
        });

        // 问题4解决方案：无效翻译装饰器
        // 仅改变文本颜色，保持原始格式
        this.untranslatedDecorationType = vscode.window.createTextEditorDecorationType({
            color: '#F44747'
        });

        // 监听文档变化以使缓存失效
        vscode.workspace.onDidChangeTextDocument(event => {
            // 清除修改文档的缓存
            this.documentCache.delete(event.document.uri.toString());
            // 如果变化发生在当前编辑器，更新装饰器
            if (this.activeEditor && event.document === this.activeEditor.document) {
                this.updateDecorations(this.activeEditor);
            }
        });

        // 监听编辑器切换以更新装饰器
        vscode.window.onDidChangeActiveTextEditor(editor => {
            this.activeEditor = editor;
            // 仅处理 Python 文件
            if (editor && editor.document.languageId === 'python') {
                this.updateDecorations(editor);
            }
        });

        // 监听光标位置变化以实现点击切换功能
        vscode.window.onDidChangeTextEditorSelection(event => {
            if (event.textEditor === this.activeEditor) {
                const position = event.selections[0].active;
                // 光标位置改变时更新最后点击位置
                if (!this.lastClickedPosition ||
                    position.line !== this.lastClickedPosition.line ||
                    position.character !== this.lastClickedPosition.character) {
                    this.lastClickedPosition = position;
                    this.updateDecorations(event.textEditor);
                }
            }
        });

        // 初始化时处理当前活动编辑器
        if (vscode.window.activeTextEditor) {
            this.activeEditor = vscode.window.activeTextEditor;
            this.updateDecorations(this.activeEditor);
        }
    }

    private getCachedMatches(document: vscode.TextDocument): DocumentCache['matches'] {
        const uri = document.uri.toString();
        const cache = this.documentCache.get(uri);

        // 性能优化：
        // 如果文档未改变，返回缓存结果
        // 这避免了重复解析相同的文档
        if (cache && cache.version === document.version) {
            return cache.matches;
        }

        // 如果缓存无效，解析文档中的翻译键
        const matches: DocumentCache['matches'] = [];
        for (let i = 0; i < document.lineCount; i++) {
            const line = document.lineAt(i).text;
            let match;

            this.output.appendLine(`\n[调试] 处理第 ${i} 行: ${line}`);

            // 重置正则表达式的 lastIndex 以确保正确匹配
            I18nDefinitionProvider.T_CALL_PATTERN.lastIndex = 0;

            // 查找当前行中的所有翻译键
            // 使用匹配整个 t("key") 调用的正则表达式
            while ((match = I18nDefinitionProvider.T_CALL_PATTERN.exec(line)) !== null) {
                this.output.appendLine(`[调试] 找到匹配:`);
                this.output.appendLine(`  完整匹配: "${match[0]}"`);
                this.output.appendLine(`  键: "${match[1]}"`);
                this.output.appendLine(`  起始索引: ${match.index}`);
                this.output.appendLine(`  结束索引: ${match.index + match[0].length}`);

                // 存储匹配信息，包括精确位置
                // 这有助于解决问题2（范围计算）
                matches.push({
                    key: match[1],
                    startIndex: match.index,  // 从 't' 开始
                    endIndex: match.index + match[0].length,  // 包括结束括号
                    line: i
                });
            }
        }

        // 使用新结果更新缓存
        this.documentCache.set(uri, {
            version: document.version,
            matches
        });

        return matches;
    }

    private async updateDecorations(editor: vscode.TextEditor) {
        // 仅处理 Python 文件
        if (editor.document.languageId !== 'python') {
            return;
        }

        // 清除现有装饰器以防止过时的装饰
        editor.setDecorations(this.translationDecorationType, []);
        editor.setDecorations(this.hideOriginalDecorationType, []);
        editor.setDecorations(this.untranslatedDecorationType, []);

        const matches = this.getCachedMatches(editor.document);
        const translationDecorations: vscode.DecorationOptions[] = [];
        const hideDecorations: vscode.DecorationOptions[] = [];
        const untranslatedDecorations: vscode.DecorationOptions[] = [];

        for (const match of matches) {
            const translation = this.i18nService.getTranslation(match.key);

            // 问题2解决方案：确保范围完全覆盖 t("key") 调用
            // 计算从 't' 开始到 ')' 结束的精确范围
            const startPos = new vscode.Position(match.line, match.startIndex);
            const endPos = new vscode.Position(match.line, match.endIndex);
            const range = new vscode.Range(startPos, endPos);

            if (translation && translation.value) {
                // 问题6解决方案：跳过点击位置的装饰器
                // 这允许在点击时查看原始文本
                if (this.lastClickedPosition && range.contains(this.lastClickedPosition)) {
                    continue;
                }

                // 问题1解决方案第1部分：添加翻译文本装饰器
                // 在原始文本前显示绿色翻译文本
                const translationDecoration: vscode.DecorationOptions = {
                    range,
                    renderOptions: {
                        before: {
                            contentText: `t("${translation.value}")`,
                            color: '#6A9955'
                        }
                    }
                };
                translationDecorations.push(translationDecoration);

                // 问题1解决方案第2部分：添加隐藏装饰器
                // 在有翻译的地方隐藏原始文本
                const hideDecoration: vscode.DecorationOptions = {
                    range
                };
                hideDecorations.push(hideDecoration);
            } else {
                // 问题4解决方案：处理无效翻译
                // 显示红色原始文本而不是应用装饰器
                untranslatedDecorations.push({ range });
            }
        }

        // 问题5解决方案：按正确顺序应用装饰器
        // 1. 首先隐藏原始文本
        // 2. 其次显示翻译文本
        // 3. 最后应用无效翻译样式
        editor.setDecorations(this.hideOriginalDecorationType, hideDecorations);
        editor.setDecorations(this.translationDecorationType, translationDecorations);
        editor.setDecorations(this.untranslatedDecorationType, untranslatedDecorations);
    }

    public async provideDefinition(
        document: vscode.TextDocument,
        position: vscode.Position,
        _token: vscode.CancellationToken
    ): Promise<vscode.Definition | null> {
        this.output.appendLine(`\n[Definition Provider] Triggered for file: ${document.fileName}`);
        this.output.appendLine(`Language ID: ${document.languageId}`);
        this.output.appendLine(`Position: ${position.line}:${position.character}`);

        if (document.languageId === 'python') {
            this.output.appendLine('Handling Python file definition');
            return this.providePythonDefinition(document, position);
        } else if (document.languageId === 'json' && document.fileName.includes('i18n')) {
            this.output.appendLine('Handling JSON file definition');
            return this.provideJsonDefinition(document, position);
        }
        this.output.appendLine('File type not supported');
        return null;
    }

    private async providePythonDefinition(
        document: vscode.TextDocument,
        position: vscode.Position
    ): Promise<vscode.Definition | null> {
        // First check the current line for better performance
        const matches = this.getCachedMatches(document);
        const matchInPosition = matches.find(match =>
            match.line === position.line &&
            position.character >= match.startIndex &&
            position.character <= match.endIndex
        );

        if (matchInPosition) {
            const translation = this.i18nService.getTranslation(matchInPosition.key);

            if (translation) {
                this.output.appendLine(`Found translation at ${translation.filePath}:${translation.line}`);
                return new vscode.Location(
                    vscode.Uri.file(translation.filePath),
                    new vscode.Position(translation.line, 0)
                );
            }

            // Directly show input box for adding translation
            const value = await vscode.window.showInputBox({
                prompt: `Enter translation value for key "${matchInPosition.key}"`,
                placeHolder: 'Translation value'
            });

            if (value) {
                const success = await this.i18nService.addTranslation(matchInPosition.key, value);
                if (success) {
                    vscode.window.showInformationMessage(`Translation added: ${matchInPosition.key} = ${value}`);
                    // Refresh decorations
                    if (this.activeEditor) {
                        this.updateDecorations(this.activeEditor);
                    }
                }
            }
        }

        return null;
    }

    private async provideJsonDefinition(
        document: vscode.TextDocument,
        position: vscode.Position
    ): Promise<vscode.Definition | null> {
        const line = document.lineAt(position.line).text;
        this.output.appendLine(`[JSON Definition] Processing line: ${line}`);

        // First try to get the key at cursor
        const wordRange = document.getWordRangeAtPosition(position, /['"]([\w\.]+)['"]/);
        if (!wordRange) {
            this.output.appendLine('No valid word range found at cursor position');
            // Try alternative method: look for the key in the whole line
            const keyMatch = /"([^"]+)":\s*"[^"]*"/.exec(line);
            if (keyMatch) {
                const keyStart = line.indexOf(keyMatch[1]);
                const keyEnd = keyStart + keyMatch[1].length;
                if (position.character >= keyStart && position.character <= keyEnd) {
                    this.output.appendLine(`Found key using line regex: ${keyMatch[1]}`);
                    const fullKey = await this.getFullKeyPath(document, position.line, keyMatch[1]);
                    if (fullKey) {
                        return this.findPythonUsage(fullKey);
                    }
                }
            }
            return null;
        }

        // Extract the key from the current line
        const lineText = line.substring(wordRange.start.character, wordRange.end.character);
        const key = lineText.replace(/['"]/g, '');
        this.output.appendLine(`Extracted key: ${key}`);

        // Get the full key path
        const fullKey = await this.getFullKeyPath(document, position.line, key);
        if (!fullKey) {
            this.output.appendLine('Failed to build full key path');
            return null;
        }
        this.output.appendLine(`Built full key path: ${fullKey}`);

        return this.findPythonUsage(fullKey);
    }

    private async getFullKeyPath(document: vscode.TextDocument, currentLine: number, lastKey: string): Promise<string | null> {
        try {
            // Parse the entire document as JSON
            const text = document.getText();
            const jsonObj = JSON.parse(text);

            // Find the line position in the original text
            const lineText = document.lineAt(currentLine).text;
            const position = text.indexOf(lineText);

            // Build the path by traversing the object
            const paths: string[][] = [];
            const findKey = (obj: any, currentPath: string[] = []): void => {
                for (const key in obj) {
                    const newPath = [...currentPath, key];
                    const keyPosition = text.indexOf(`"${key}"`, position - 1000); // Look back up to 1000 chars

                    if (key === lastKey && Math.abs(keyPosition - position) < lineText.length) {
                        paths.push(newPath);
                    }

                    if (typeof obj[key] === 'object' && obj[key] !== null) {
                        findKey(obj[key], newPath);
                    }
                }
            };

            findKey(jsonObj);

            // If we found multiple paths, find the one closest to our position
            if (paths.length > 0) {
                if (paths.length === 1) {
                    return paths[0].join('.');
                }

                // Find the path that best matches the context
                let bestPath = paths[0];
                let bestDistance = Infinity;

                for (const path of paths) {
                    const fullKey = path.join('.');
                    const keyPosition = text.lastIndexOf(fullKey, position);
                    if (keyPosition !== -1) {
                        const distance = Math.abs(position - keyPosition);
                        if (distance < bestDistance) {
                            bestDistance = distance;
                            bestPath = path;
                        }
                    }
                }

                return bestPath.join('.');
            }

            return null;
        } catch (error) {
            this.output.appendLine(`Error parsing JSON: ${error}`);
            return null;
        }
    }

    private fileCache = new Map<string, string>();

    private async findPythonUsage(translationKey: string): Promise<vscode.Definition | null> {
        this.output.appendLine(`[Find Usage] Searching for key: ${translationKey}`);

        // Find all Python files in specific directories
        const files = await vscode.workspace.findFiles(
            '{app,tests}/**/*.py',  // Only search in app and tests directories
            '**/node_modules/**'
        );
        this.output.appendLine(`Found ${files.length} Python files to search`);

        // Escape special characters in the key for regex
        const escapedKey = translationKey.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
        const pattern = new RegExp(`t\\(['"](${escapedKey})['"](\\)|,|\\s|;)`, 'g');

        // First try exact match with caching
        for (const file of files) {
            this.output.appendLine(`Searching in file: ${file.fsPath}`);

            // Use cached content if available
            let content = this.fileCache.get(file.fsPath);
            if (!content) {
                const document = await vscode.workspace.openTextDocument(file);
                content = document.getText();
                this.fileCache.set(file.fsPath, content);
            }

            const matches = Array.from(content.matchAll(pattern));
            if (matches.length > 0) {
                const match = matches[0];
                const document = await vscode.workspace.openTextDocument(file);
                const pos = document.positionAt(match.index!);
                this.output.appendLine(`Found usage at ${file.fsPath}:${pos.line + 1}`);
                return new vscode.Location(file, pos);
            }
        }

        // If no exact match found, try partial match (for nested keys)
        const keyParts = translationKey.split('.');
        while (keyParts.length > 1) {
            keyParts.shift(); // Remove the first part
            const partialKey = keyParts.join('.');
            const escapedPartialKey = partialKey.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
            const partialPattern = new RegExp(`t\\(['"](${escapedPartialKey})['"](\\)|,|\\s|;)`, 'g');

            this.output.appendLine(`[Find Usage] Trying partial key: ${partialKey}`);

            for (const file of files) {
                // Use cached content
                const content = this.fileCache.get(file.fsPath)!; // Content is already cached from first pass
                const matches = Array.from(content.matchAll(partialPattern));

                if (matches.length > 0) {
                    const match = matches[0];
                    const document = await vscode.workspace.openTextDocument(file);
                    const pos = document.positionAt(match.index!);
                    this.output.appendLine(`Found usage with partial key at ${file.fsPath}:${pos.line + 1}`);
                    return new vscode.Location(file, pos);
                }
            }
        }

        this.output.appendLine('No usage found');
        return null;
    }
} 